package com.zbl.springdemo.config.aspect;

import com.zbl.springdemo.config.annotation.DynamicParam;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class SpelAspect {
 
    private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
 
    @Around(value = "@annotation(dynamicParam)")
    public Object lock(ProceedingJoinPoint joinPoint, DynamicParam dynamicParam) throws Throwable {
        // 解析出注解里的动态参数
        Object param = getDynamicParam(joinPoint, dynamicParam);
        try{
            // do something...
            return joinPoint.proceed();
        }catch (Throwable e) {
            log.error(e.getMessage(),e);
            return null;
        }
    }
 
    /**
     * 解析出注解里的动态参数
     * @param joinPoint
     * @param dynamicParam
     * @return
     */
    private Object getDynamicParam(ProceedingJoinPoint joinPoint, DynamicParam dynamicParam) {
        // spel解析器
        ExpressionParser parser = new SpelExpressionParser();
        // spel表达式对象
        Expression expression = parser.parseExpression(dynamicParam.key());
        // 上下文对象
        EvaluationContext context = new MethodBasedEvaluationContext(TypedValue.NULL, resolveMethod(joinPoint),
                joinPoint.getArgs(), parameterNameDiscoverer);
        // 解析出的动态参数
        return expression.getValue(context);
    }
 
    /**
     * 获取注解所在的method
     * @param joinPoint
     * @return
     */
    private Method resolveMethod(ProceedingJoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class<?> targetClass = joinPoint.getTarget().getClass();
 
        return getDeclaredMethodFor(targetClass, signature.getName(),
                signature.getMethod().getParameterTypes());
    }
 
    private Method getDeclaredMethodFor(Class<?> clazz, String name, Class<?>... parameterTypes) {
        try {
            return clazz.getDeclaredMethod(name, parameterTypes);
        } catch (NoSuchMethodException e) {
            Class<?> superClass = clazz.getSuperclass();
            if (superClass != null) {
                return getDeclaredMethodFor(superClass, name, parameterTypes);
            }
        }
        throw new IllegalStateException("Cannot resolve target method: " + name);
    }
 
}